package com.lechi.web.listener;

import com.alibaba.fastjson2.JSON;
import com.google.common.io.Files;
import com.lechi.iot.netty.utils.BytesToHexUtil;
import com.lechi.web.common.config.LeChiConfig;
import com.lechi.web.common.utils.DateUtils;
import com.lechi.web.constants.Constants;
import com.lechi.web.framework.log.IotExceptionLog;
import com.lechi.web.framework.manager.AsyncManager;
import com.lechi.web.record.FTPConfig;
import com.lechi.web.service.IIotFtpUploadsService;
import com.lechi.web.service.IIotSensorRecordService;
import com.lechi.web.system.domain.IotFtpUploads;
import com.lechi.web.system.domain.IotInterfaceApiBo;
import com.lechi.web.system.domain.IotSensorRecordBo;
import com.lechi.web.system.service.IIotInterfaceApiService;
import io.netty.channel.epoll.Epoll;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.contentobjects.jnotify.JNotify;
import net.contentobjects.jnotify.JNotifyAdapter;
import net.contentobjects.jnotify.JNotifyException;
import org.apache.commons.net.ftp.FTPClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.*;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;


/**
 * The type Jnotify adapter.
 */
@Slf4j
@Component
public class FileJnotifyAdapter extends JNotifyAdapter {

    private static final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(10);

    /**
     * ftp目录监听程序Id
     */
    public int ftpImageWatchID;

    /**
     * 传感器目录监听程序Id
     */
    public int recordWatchID;

    /**
     * 关注目录的事件
     */
    int mask = JNotify.FILE_CREATED | JNotify.FILE_DELETED;

    /**
     * 是否监视子目录，即级联监视
     */
    boolean watchSubtree = true;

    @Getter
    private String uploadPath;

    @Autowired
    private IIotFtpUploadsService iotFtpUploadsService;

    @Autowired
    private IIotSensorRecordService iIotSensorRecordService;

    @Autowired
    private IIotInterfaceApiService iotInterfaceApiService;


    private static boolean verifyJpgUpload(String filePath) {
        File file = new File(filePath);
        try (FileInputStream fis = new FileInputStream(file)) {
            long fileLength = file.length();
            if (fileLength < 2) {
                return false;
            }
            // 移动到倒数第二个字节
            fis.skip(fileLength - 2);
            byte[] lastTwoBytes = new byte[2];
            fis.read(lastTwoBytes);
            String hexBytes = BytesToHexUtil.bytesToHexString(lastTwoBytes).toUpperCase();
            return "FFD9".equals(hexBytes);
        } catch (IOException e) {
            log.error("验证过程中出现错误:", e);
            return false;
        }
    }


    /**
     * 容器启动时启动监视程序
     */
    public void beginWatch() {
        try {
            this.ftpImageWatchID = JNotify.addWatch(LeChiConfig.getFtpUploadPath(), mask, watchSubtree, this);
            this.recordWatchID = JNotify.addWatch(LeChiConfig.getRecordLogPath(), mask, watchSubtree, this);
            log.error("FileJnotifyAdapter启动成功, FTPWatchID={}----------RecordWatchID={}", ftpImageWatchID, recordWatchID);
        } catch (JNotifyException e) {
            log.error("FileJnotifyAdapter beginWatch", e);
        }
        /**
         * 死循环，线程一直执行，休眠一分钟后继续执行，主要是为了让主线程一直执行 休眠时间和监测文件发生的效率无
         * （就是说不是监视目录文件改变一分钟后才监测到，监测几乎是实时的，调用本地系统库）
         */
        while (true) {
            try {
                Thread.sleep(60000);
            } catch (InterruptedException e) {
                log.error("FileJnotifyAdapter beginWatch", e);
            }
        }
    }

    /**
     * 文件创建
     *
     * @param watchID  监听程序Id 初始为1，多个监控程序以此加1
     * @param rootPath 目录名
     * @param name     文件名
     */
    @Override
    public void fileCreated(int watchID, String rootPath, String name) {
        String filePath = rootPath + File.separator + name;
        log.error("watchID:{} 文件被创建:{}, 创建位置为： {}", watchID, File.separator, filePath);
        String separator = File.separator;
        String[] paths = Epoll.isAvailable() ? name.split(separator) : name.split("\\\\");
        if (watchID == ftpImageWatchID) {
            var iotFtpUploads = new IotFtpUploads();
            try {
                if (filePath.endsWith(".jpg") || filePath.endsWith(".png") || filePath.endsWith(".jpeg")) {
                    iotFtpUploads.setDeviceStation(paths[0]);
                    iotFtpUploads.setFilepath(filePath);
                    iotFtpUploads.setFilename(name);
                    String fileName = paths[paths.length - 1];
                    if (fileName.substring(0, fileName.lastIndexOf(".")).length() == 14) {
                        log.error("fileName.substring(1, 13) + 00 ： {}", fileName.substring(1, 13) + "00");
                        Date createTime = DateUtils.parseDate(fileName.substring(1, 13) + "00", DateUtils.YYYYMMDDHHMMSS);
                        iotFtpUploads.setCreateTime(createTime);
                    } else {
                        String dateTime = fileName.substring(fileName.lastIndexOf("_") + 1, fileName.lastIndexOf("."));
                        Date createTime = DateUtils.parseDate(dateTime, DateUtils.YYYYMMDDHHMMSS);
                        iotFtpUploads.setCreateTime(createTime);
                    }
//                    iotFtpUploadsService.insertIotFtpUploads(iotFtpUploads);
                    delayUpload(iotFtpUploads);
                }
            } catch (Exception e) {
                StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[1];
                String method = Thread.currentThread().getName() + ":" + stackTraceElement.getClassName() + "." + stackTraceElement.getMethodName();
                CompletableFuture.runAsync(IotExceptionLog.exceptionLog(e, "Ftp文件notify监听服务异常", method, iotFtpUploads));
            }
        }
        if (watchID == recordWatchID) {
            var IotSensorRecordBo = new IotSensorRecordBo();
            try {
                if (filePath.endsWith(".csv") || filePath.endsWith(".txt")) {
                    IotSensorRecordBo.setFilepath(filePath);
                    IotSensorRecordBo.setFilename(name);
                    IotSensorRecordBo.setDeviceId(Long.parseLong(paths[0]));
                    IotSensorRecordBo.setCreateTime(new Date());
                    iIotSensorRecordService.insertIotSensorRecord(IotSensorRecordBo);
                }
            } catch (Exception e) {
                StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[1];
                String method = Thread.currentThread().getName() + ":" + stackTraceElement.getClassName() + "." + stackTraceElement.getMethodName();
                CompletableFuture.runAsync(IotExceptionLog.exceptionLog(e, "站点生成数据文件后添加记录异常", method, IotSensorRecordBo));
            }
        }
    }

    private void delayUpload(IotFtpUploads iotFtpUploads) {
//        log.error("延迟一分钟上传对方ftp服务器=====uploadDelay============================： {}", iotFtpUploads);
        CompletableFuture.runAsync(() -> {
            try {
                executorService.schedule(() -> {
                    try {
                        if (verifyJpgUpload(iotFtpUploads.getFilepath())) {
                            uploadToFtp(iotFtpUploads);
                        }
                    } catch (Exception e) {
                        log.error("uploadImage error", e);
                    }
                    iotFtpUploadsService.insertIotFtpUploads(iotFtpUploads);
                }, 1, TimeUnit.MINUTES);
            } catch (Exception e) {
                log.error("uploadImage error", e);
            }
        });
    }

    public void uploadToFtp(IotFtpUploads iotFtpUploads) {
        iotFtpUploads.setUploadTime(new Date());
        IotInterfaceApiBo iotInterfaceApiBo = new IotInterfaceApiBo();
        iotInterfaceApiBo.setDeviceStation(iotFtpUploads.getDeviceStation());
        List<IotInterfaceApiBo> list = iotInterfaceApiService.selectAuthInterfaceDevice(iotInterfaceApiBo);
        if (list.isEmpty() || !Constants.TRIGGER_OPEN_STATUE.equals(list.get(0).getStatus())) {
            log.error("没有找到对应的接口信息，设备号：{}, iotFtpUploads：{}", iotFtpUploads.getDeviceStation(), JSON.toJSONString(iotFtpUploads));
            return;
        }
        FTPConfig ftpConfig = JSON.parseObject(list.get(0).getInterfaceContent(), FTPConfig.class);
        boolean isStore = ftpUpload(ftpConfig, iotFtpUploads);
        iotFtpUploads.setStatus(isStore ? 1 : 0);
    }


    private static boolean ftpUpload(FTPConfig ftpConfig, IotFtpUploads iotFtpUploads) {
        final String filePath = iotFtpUploads.getFilename();
        FTPClient ftpClient = new FTPClient();
        boolean isStore = false;
        try {
            ftpClient.connect(ftpConfig.ftphost(), ftpConfig.ftpport());
            ftpClient.login(ftpConfig.ftpuser(), ftpConfig.ftppassword());
            ftpClient.enterLocalPassiveMode();
            String[] dirs = filePath.substring(0, filePath.lastIndexOf("/")).split("/");
            String currentDir = "/";
            boolean success = ftpClient.changeWorkingDirectory(currentDir + filePath.substring(0, filePath.lastIndexOf("/")));
            if (!success) {
                for (String dirName : dirs) {
                    if (!dirName.isEmpty()) {
                        currentDir += dirName + "/";
                        success = ftpClient.changeWorkingDirectory(currentDir);
                        if (!success) {
                            success = ftpClient.makeDirectory(dirName);
                            if (success) {
                                ftpClient.changeWorkingDirectory(dirName);
                            } else {
                                log.error("Failed to create directory: {}", dirName);
                            }
                        }
                    }
                }
            }
            byte[] bytes = Files.toByteArray(new File(iotFtpUploads.getFilepath()));
            InputStream inputStream = new ByteArrayInputStream(bytes);
            ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
            isStore = ftpClient.storeFile(filePath.substring(filePath.lastIndexOf("/") + 1), inputStream);
            log.error("文件上传成功:{}", isStore + "===" + filePath);
            ftpClient.disconnect();
        } catch (Exception e) {
            StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[1];
            String method = Thread.currentThread().getName() + ":" + stackTraceElement.getClassName() + "." + stackTraceElement.getMethodName();
            AsyncManager.me().execute(IotExceptionLog.exceptionLog(e, "图片文件上传ftp产生异常", method, ftpConfig));
            log.error("图片文件上传ftp产生异常:", e);
        } finally {
            try {
                if (ftpClient.isConnected()) {
                    ftpClient.logout(); // 登出
                    ftpClient.disconnect(); // 断开连接
                }
            } catch (IOException e) {
                log.error("ftpClient 关闭连接产生异常:", e);
            }
        }
        return isStore;
    }
}
